package v2.parser.cli

import java.io.Writer
import util.parsing.combinator.JavaTokenParsers
import scala.util.parsing.input.Reader
import v2.parser.tree.NodeCrud
import v2.parser.load.{Saver, Loader}
import v2.parser._

/**
 * @author bweber
 * @version $Rev$, $Date$
 */

class Commands(var tree:NodeCrud) extends MoreToParse {

  def command : Parser[MoreToParse] = cd | ls | pwd | istype | types | _type | setter | getter | save | load | clear | delete | help
  def cd      : Parser[MoreToParse] = "cd" ^^ { a => new CdParser(tree) }
  def ls      : Parser[MoreToParse] = "ls" ^^ { a => new LsParser(tree) }
  def pwd     : Parser[MoreToParse] = "pwd" ^^ { a => new PwdParser(tree) }
  def _type   : Parser[MoreToParse] = "type" ^^ { a => new TypeParser(tree) }
  def istype  : Parser[MoreToParse] = "type?" ^^ { a => new IsTypeParser(tree) }
  def types   : Parser[MoreToParse] = "types" ^^ { a => new ListTypesParser(tree) }
  def setter  : Parser[MoreToParse] = "set" ^^ { a => new SetterParser(tree) }
  def getter  : Parser[MoreToParse] = "get" ^^ { a => new GetterParser(tree) }
  def save    : Parser[MoreToParse] = "save" ^^ { a => new SaveParser(tree) }
  def load    : Parser[MoreToParse] = "load" ^^ { a => new LoadParser(tree,this) }
  def clear   : Parser[MoreToParse] = "clear" ^^ { a => new ClearParser(tree) }
  def delete  : Parser[MoreToParse] = "delete" ^^ { a => new DeleteParser(tree) }
  def help    : Parser[MoreToParse] = "help" ^^ { a => new DetailedHelp(tree) }
  
  def parse(line:Reader[Char],writer:Writer):ParseResult[MoreToParse] = {
    parse(command,line)
  }
}

trait MoreToParse extends JavaTokenParsers {

  implicit def quotableString(originalString:String) = new ScalaObject with Quotable {
     str = originalString
   }
  
  var tree:NodeCrud
  def parse(remaining:Reader[Char],writer:Writer):ParseResult[Any] 
  def parseMore(line:Reader[Char],writer:Writer):ParseResult[Any] = {
        val thisResult = parse(line,writer)
        thisResult match {
          case Success(result,remaining) => 
            if ( result != null && result.isInstanceOf[MoreToParse]) {
              val next = result.asInstanceOf[MoreToParse]
              next.parseMore(remaining,writer)
            }
            else {
              // done
                var linevar = remaining
                var rest = ""
                while ( !linevar.atEnd) {
                  linevar = linevar.rest
                  rest += linevar.first
                }
                if ( !rest.isEmpty ) {
                  writer.write("IGNORING " + rest)
                }
            }
          case x => 
            println("Invalid command, try 'help' for usage [" + x.asInstanceOf[Failure].msg + "]")
        }
      thisResult
  }
}

class DetailedHelp(var tree:NodeCrud) extends MoreToParse {
  
  def helps : Parser[String] = ls | cd
  def ls : Parser[String] = "ls" ^^ { a => "Lists the contents of the current location" }
  def cd : Parser[String] = "cd" ^^ { a => "Changes to the specified location" }
  // pwd
  // type
  // type?
  // types
  // get
  // set
  // save
  // load
  // clear
  // delete
  
  def parse(line:Reader[Char],writer:Writer):ParseResult[String] = {
    val detailed = parse(helps,line)
    detailed match {
      case Success(result,remaining) => 
        writer.write(result)
        Success(null,remaining)
      case _ => 
    writer.write("""Things you can do:
    ls              - list contents of current location
    ls   "dir"+     - list contents of specificed location
    pwd             - current location
    cd   "dir"+     - move to the specified location
    type            - prints the type of the current location
    type?           -
    types           -
    set             -
    get             -
    save            -
    load            -
    clear           -
    delete          -
    help            - this generic help message
    help <command>  - for more specific details about a command from this list
    quit            - exits the shell
    exit            - exits the shell
            """)
      Success(null,line)
    }
  }
}

class CdParser(var tree:NodeCrud) extends MoreToParse {
  def up   : Parser[String] = ".."
  def root : Parser[String] = "/"
  def dirs : Parser[List[String]] = rep(dir)
  def dir  : Parser[String] = stringLiteral ^^ { _.unquote } 
  
  def parse(oline:Reader[Char],writer:Writer):ParseResult[Any] = {
    var line = oline
    parse(up,line) match {
      case Success(result,remainder) =>
        tree.up_cd
        writer.write(tree.pwd.mkString(","))
        return Success(null,remainder)
      case _ => // try something else
    }
    parse(root,line) match {
      case Success(result,remainder) =>
        tree.root_cd
        writer.write(tree.pwd.mkString(","))
        line = remainder
      case _ => // see if there is more
    }
    val result = parse(dirs,line)
    val list = result.get
    if ( list.isEmpty ) {
      var str = ""
      var linevar = line
      while ( !linevar.atEnd) {
        linevar = linevar.rest
        str += linevar.first
      }
      if ( !str.trim.isEmpty ) {
        writer.write("unable to cd to " + str)
      }
    }
    else {
      try {
        tree.relative_cd(list:_*)
        writer.write(tree.pwd.mkString(","))
      }
      catch {
        case e:Exception => 
          writer.write("unable to cd [" + e.getMessage + "]")
      }
    }
    result
  }
}

class LsParser(var tree:NodeCrud) extends MoreToParse {
  def parse(line:Reader[Char],writer:Writer):ParseResult[Any] = {
     writer.write(tree.ls.mkString(","))
    Success(null,line)
  }
}

class PwdParser(var tree:NodeCrud) extends MoreToParse {
  def parse(line:Reader[Char],writer:Writer):ParseResult[Any] = {
    writer.write(tree.pwd.mkString(","))
    Success(null,line)
  }
}

class TypeParser(var tree:NodeCrud) extends MoreToParse {
  def parse(line:Reader[Char],writer:Writer):ParseResult[Any] = {
    writer.write(tree.current_type)
    Success(null,line)
  }
}

class LoadParser(var tree:NodeCrud,commands:Commands) extends MoreToParse {
  def parse(line:Reader[Char],writer:Writer):ParseResult[Any] = {
    commands.tree = NodeCrud(Loader().currentTree)
    Success(null,line)
  }
}

class SaveParser(var tree:NodeCrud) extends MoreToParse {
  def parse(line:Reader[Char],writer:Writer):ParseResult[Any] = {
    Saver(tree)
    Success(null,line)
  }
}

class IsTypeParser(var tree:NodeCrud) extends MoreToParse {
  
  def typeName : Parser[String] = stringLiteral ^^ { _.unquote }
  
  def parse(line:Reader[Char],writer:Writer):ParseResult[Any] = {
    parse(typeName,line) match {
      case Success(result,remaining) =>
        if ( Type.isType(result)) {
          writer.write(result + " is a type")
        }
        else {
          writer.write(result + " is NOT a type")
        }
        return Success(null,remaining)
    }
    throw new RuntimeException("invalid is type request")
  }
}

class ListTypesParser(var tree:NodeCrud) extends MoreToParse {
  def parse(line:Reader[Char],writer:Writer):ParseResult[Any] = {
    writer.write(Type.listTypeNames.mkString(", "))
    Success(null,line)
  }
}


class ClearParser(var tree:NodeCrud) extends MoreToParse {
  
  def attrName : Parser[String] = stringLiteral ^^ { _.unquote }
  
  def parse(line:Reader[Char],writer:Writer):ParseResult[Any] = {
     parse(attrName,line) match {
       case Success(result,remaining) => 
         tree.clearAttr(result)
         Success(null,remaining)
     }
  }
}

class DeleteParser(var tree:NodeCrud) extends MoreToParse {
  
  def attrName : Parser[String] = stringLiteral ^^ { _.unquote }
  
  def parse(line:Reader[Char],writer:Writer):ParseResult[Any] = {
     parse(attrName,line) match {
       case Success(result,remaining) => 
         tree.deleteAttr(result)
         Success(null,remaining)
     }
  }
}

class GetterParser(var tree:NodeCrud) extends MoreToParse {
  
  def attrName : Parser[String] = stringLiteral ^^ { _.unquote }
  
  def parse(line:Reader[Char],writer:Writer):ParseResult[Any] = {
    parse(attrName,line) match {
      case Success(result,remaining) =>
        if ( tree.currentTree.currentNode.isInstanceOf[Single] ) {
          writer.write(tree.currentTree.currentNode.asInstanceOf[Single].getAttr(result).toString)
          return Success(null,remaining)
        }
        else if ( tree.currentTree.currentNode.isInstanceOf[Bunch] ) {
          for ( slot <- tree.currentTree.currentNode.asInstanceOf[Bunch].slots ) {
            if ( slot.attrref == result ) {
              val value = slot.value
              if ( value.isInstanceOf[Literal]){
                writer.write(value.asInstanceOf[Literal].value)
              }
              else {
                writer.write(value.toString)
              }
            }
          }
          return Success(null,remaining)
        }
        else if ( tree.currentTree.currentNode.isInstanceOf[Type] ) {
          writer.write(tree.currentTree.currentNode.asInstanceOf[Type].getAttribute(result).toString)
          return Success(null,remaining)
        }
        else if ( tree.currentTree.currentNode.isInstanceOf[Literal] ) {
          writer.write(tree.currentTree.currentNode.asInstanceOf[Literal].value)
          return Success(null,remaining)
        }
    }
    throw new RuntimeException("Unable to get attribute.")
  }
}

class SetterParser(var tree:NodeCrud) extends MoreToParse {
  
  def attrName : Parser[String] = stringLiteral ^^ { _.unquote }
  
  def parse(line:Reader[Char],writer:Writer):ParseResult[Any] = {
    parse(attrName,line) match {
      case Success(result,remaining) =>
        val vp = new ValueParser(tree,result)
        vp.parse(remaining,writer) match {
          case vp.Success(result2,remaining2) =>
            return Success(null,remaining2)
        }
    }
    throw new RuntimeException("invalid setter")
  }
}

class ValueParser(var tree:NodeCrud, val attrName:String) extends MoreToParse {
  
  def literal    : Parser[String]    = stringLiteral ^^ { _.unquote }
  def single     : Parser[TypeRef]   = "single" ~> stringLiteral ^^ { str => Type.typeRef(str.unquote) }
  def bunch      : Parser[TypeRef]   = "bunch" ~> stringLiteral ^^ { str => Type.typeRef(str.unquote) } 
  def _type      : Parser[String]    = "type" ~> stringLiteral ^^ { _.unquote }
  def abslink    : Parser[List[String]] = "link" ~> "/" ~> rep(path)
  def rellink    : Parser[List[String]] = "link" ~> rep(path)
  def path       : Parser[String]    = stringLiteral ^^ { _.unquote }
  
  def parse(line:Reader[Char],writer:Writer):ParseResult[Value] = {
     parse(literal,line) match {
       case Success(result,remaining) =>
           return Success(tree.newLiteral(attrName,result),remaining)
       case _ =>
     }
     parse(single,line) match {
       case Success(result,remaining) =>
         return Success(tree.newSingle(attrName,result.name),remaining)
       case _ =>
     }
     parse(bunch,line) match {
       case Success(result,remaining) =>
         return Success(tree.newBunch(attrName,result.name),remaining)
       case _ =>
     }
     parse(_type,line) match {
       case Success(result,remaining) =>
         return Success(tree.newType(attrName,result),remaining)
       case _ => 
     }
     parse(abslink,line) match {
       case Success(result,remaining) =>
        return Success(tree.newAbsoluteLink(attrName,result),remaining)
       case _ =>
     }
     parse(rellink,line) match {
       case Success(result,remaining) =>
        return Success(tree.newRelativeLink(attrName,result),remaining)
     }
  }
}
